/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*                                                                    *
*        ***************************************************         *
*        *©2021-2031 LINUO Corporation All rights reserved *         *
*        ***************************************************         *
*                                                                    *
* FileName    : vi.c                                                 *
*                                                                    *
* Author      : linus                                                *
*                                                                    *
* Email       : luoyaojun@sina.com                                   *
*                                                                    *
* Date        : 2024-3-27                                            *
*                                                                    *
* Description :                                                      *
*                                                                    *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include "vi.h"
#include "h_register.h"
#include "h_vi.h"

static HT_S32 rockchip_vi_check_id_availd(HT_S32 viDev, HT_S32 viChn, HT_S32 viPipe)
{
	if (viDev < 0 || viDev >= VI_MAX_DEV_NUM)
	{
		printf("VI check viDev id error.\n");
		return -1;
	}
	
	if (viChn < 0 || viChn >= VI_MAX_CHN_NUM)
	{
		printf("VI check viChn id error.\n");
		return -2;
	}
	
	if (viPipe < 0 || viPipe >= VI_MAX_PIPE_NUM)
	{
		printf("VI check viPipe id error.\n");
		return -3;
	}
	
	return 0;
}

static VI_V4L2_MEMORY_TYPE rockchip_vi_memory_type(HT_MEMORY_TYPE type)
{
	VI_V4L2_MEMORY_TYPE mem_type = VI_V4L2_MEMORY_TYPE_MMAP;
	
	switch (type)
	{
	case HT_MEMORY_TYPE_DMABUF:
		mem_type = VI_V4L2_MEMORY_TYPE_DMABUF;
		break;
	case HT_MEMORY_TYPE_MMAP:
		mem_type = VI_V4L2_MEMORY_TYPE_MMAP;
		break;
	case HT_MEMORY_TYPE_USERPTR:
		mem_type = VI_V4L2_MEMORY_TYPE_USERPTR;
		break;
	case HT_MEMORY_TYPE_OVERLAY:
		mem_type = VI_V4L2_MEMORY_TYPE_OVERLAY;
		break;
	default:
		mem_type = VI_V4L2_MEMORY_TYPE_MMAP;
		break;
	}

	return mem_type;
}

static PIXEL_FORMAT_E rockchip_vi_pixel_format(HT_PIXEL_FORMAT fmt)
{
	PIXEL_FORMAT_E pixel_fmt = RK_FMT_YUV420SP;

	switch(fmt)
	{
	case HT_FMT_YUV420SP:
		pixel_fmt = RK_FMT_YUV420SP;
		break;
	case HT_FMT_YUV444:
		pixel_fmt = RK_FMT_YUV444;
		break;
	case HT_FMT_BGR888:
		pixel_fmt = RK_FMT_BGR888;
		break;

	case HT_FMT_RGB_BAYER_SBGGR_10BPP:
		pixel_fmt = RK_FMT_RGB_BAYER_SBGGR_10BPP;
		break;
	//	TODO add more ...
	default:
		pixel_fmt = RK_FMT_YUV420SP;
		break;
	}

	return pixel_fmt;
}

static RK_S32 rockchip_vi_dev_init(struct ht_vi *pvi, HT_S32 viDev)
{
    HT_S32 s32Ret = -1;
	struct rockchip_vi *plt_vi = container_of(pvi, struct rockchip_vi, m_vi);
	VI_DEV_ATTR_S DevAttr;
	VI_DEV_BIND_PIPE_S stBindPipe;

	if (rockchip_vi_check_id_availd(viDev, 0, 0))
		return -1;

	memset(&DevAttr, 0, sizeof(VI_DEV_ATTR_S));
	
    s32Ret = RK_MPI_VI_GetDevAttr(viDev, &DevAttr);

    if (s32Ret == RK_ERR_VI_NOT_CONFIG)
    {
        s32Ret = RK_MPI_VI_SetDevAttr(viDev, &DevAttr);
        if (s32Ret != RK_SUCCESS)
        {
            RK_LOGE("RK_MPI_VI_SetDevAttr %x", s32Ret);
            return s32Ret;
        }
    }

    s32Ret = RK_MPI_VI_GetDevIsEnable(viDev);
    if (s32Ret != RK_SUCCESS)
    {
        s32Ret = RK_MPI_VI_EnableDev(viDev);
        if (s32Ret != RK_SUCCESS)
        {
            RK_LOGE("RK_MPI_VI_EnableDev %x", s32Ret);
            return s32Ret;
        }
		
		memset(&stBindPipe, 0, sizeof(VI_DEV_BIND_PIPE_S));
        //	参考《Rockchip_Developer_Guide_MPI_VI.pdf》16页，目前指定pipe id和dev id相同即可
        stBindPipe.u32Num = viDev;
        stBindPipe.PipeId[0] = viDev;
        s32Ret = RK_MPI_VI_SetDevBindPipe(viDev, &stBindPipe);
        if (s32Ret != RK_SUCCESS)
        {
            RK_LOGE("RK_MPI_VI_SetDevBindPipe %x", s32Ret);
            return s32Ret;
        }
    }
    else
    {
        RK_LOGE("RK_MPI_VI_EnableDev already");
    }

	

    return 0;
}

static RK_S32 rockchip_vi_chn_exit(struct ht_vi *pvi, HT_S32 viDev, HT_S32 viChn)
{
    return (RK_MPI_VI_DisableChn(viDev, viChn));
}

static RK_S32 rockchip_vi_dev_exit(struct ht_vi *pvi, HT_S32 viDev)
{
    return (RK_MPI_VI_DisableDev(viDev));
}

static RK_S32 rockchip_vi_chn_init(struct ht_vi *pvi, HT_S32 viDev, HT_S32 viChn, struct ht_vi_config *pinfo)
{
    RK_S32 s32Ret = RK_FAILURE;
    VI_CHN_ATTR_S stChnAttr;
	struct rockchip_vi *plt_vi = container_of(pvi, struct rockchip_vi, m_vi);

	memset(&stChnAttr, 0, sizeof(VI_CHN_ATTR_S));
	
    stChnAttr.stSize.u32Width = pinfo->res.width;
    stChnAttr.stSize.u32Height = pinfo->res.height;
	stChnAttr.enCompressMode = COMPRESS_MODE_NONE;

    stChnAttr.stIspOpt.u32BufCount = 10;

    //	HDMI RX需要用VI_V4L2_MEMORY_TYPE_MMAP
    stChnAttr.stIspOpt.enMemoryType = rockchip_vi_memory_type(pinfo->mem_type);
    stChnAttr.stIspOpt.enCaptureType = VI_V4L2_CAPTURE_TYPE_VIDEO_CAPTURE_MPLANE; //VI_V4L2_CAPTURE_TYPE_VIDEO_CAPTURE

    //	从vi通道getchnframe时需要用到该参数
    stChnAttr.u32Depth = pinfo->depth;

    /*
    **
    **	需要和源像素格式相同
    **	设置默认值420SP后通过RK_MPI_VI_GetChnConnectInfo函数
    **	获取真实的像素格式，如果和默认值不匹配，则重新设置当前通道属性
    **
    */
    stChnAttr.enPixelFormat = rockchip_vi_pixel_format(pinfo->pixel_fmt);
    stChnAttr.stFrameRate.s32SrcFrameRate = -1;
    stChnAttr.stFrameRate.s32DstFrameRate = -1;

	if (pinfo->dev_name)
	{
		printf("rockchip_vi_chn_init open camera: %s\n", pinfo->dev_name);
    	memcpy(stChnAttr.stIspOpt.aEntityName, pinfo->dev_name, strlen(pinfo->dev_name));
	}
	
    s32Ret = RK_MPI_VI_SetChnAttr(viDev, viChn, &stChnAttr);
    if (s32Ret != RK_SUCCESS)
    {
        printf("RK_MPI_VI_SetChnAttr %x", s32Ret);
        return s32Ret;
    }

    s32Ret = RK_MPI_VI_EnableChn(viDev, viChn);
    if (s32Ret != RK_SUCCESS)
    {
        printf("RK_MPI_VI_EnableChn %x", s32Ret);
        return s32Ret;
    }
	
	printf("rockchip_vi_chn_init... done\n");

    return s32Ret;
}

#ifndef RK3588
static HT_S32 rockchip_vi_connection_info(struct ht_vi *pvi, struct ht_vi_connection_info *conn_info)
{
	HT_S32 s32Ret = -1;
	VI_CONNECT_INFO_S sinfo;

	if (!pvi || !conn_info)
		return -1;

	if (rockchip_vi_check_id_availd(conn_info->viDev, conn_info->viChn, 0))
		return -2;

	memset(&sinfo, 0, sizeof(VI_CONNECT_INFO_S));
	s32Ret = RK_MPI_VI_GetChnConnectInfo(conn_info->viDev, conn_info->viChn, &sinfo);
	if (s32Ret != RK_SUCCESS)
	{
		RK_LOGE("RK_MPI_VI_GetChnConnectInfo %x", s32Ret);
		return s32Ret;
	}

	conn_info->pixel_fmt 	= sinfo.enPixFmt;
	conn_info->status 		= sinfo.enConnect;
	conn_info->res.width 	= sinfo.u32Width;
	conn_info->res.height 	= sinfo.u32Height;

	return 0;
}
#endif

static HT_S32 rockchip_vi_pthread_chn_get_frame(struct pthread_work *pwork, HT_VOID *user)
{
	struct ht_pthread *thread = ht_pthread_work_to_pthread(pwork);
	struct rockchip_vi *plt_vi = container_of(thread, struct rockchip_vi, pthread);
	struct rockchip_vi_priv *priv = (struct rockchip_vi_priv *)user;
    VIDEO_FRAME_INFO_S stViFrame;
    VI_CHN_STATUS_S stChnStatus;
	RK_S32 s32Ret = -1;

    memset(&stViFrame, 0, sizeof(VIDEO_FRAME_INFO_S));

	s32Ret = RK_MPI_VI_GetChnFrame(priv->dev, priv->chn, &stViFrame, priv->wait_time);

	if (s32Ret == RK_SUCCESS)
	{
//        RK_MPI_VI_QueryChnStatus(priv->dev, priv->chn, &stChnStatus);
        s32Ret = priv->user_cb(priv->dev, priv->chn, user, (void *)&stViFrame);

        RK_MPI_VI_ReleaseChnFrame(priv->dev, priv->chn, &stViFrame);
	}else 
		usleep(10 *1000);

	return 0;
}

static HT_U32 rockchip_vi_priv_id(HT_S32 dev, HT_S32 chn)
{
	HT_U32 priv_id = 0;
	
	if (dev > 0)
	{
		priv_id = (dev*VI_MAX_DEV_NUM)+chn;
	}
	else
	{
		priv_id = chn;
	}
	
	return (priv_id>=VI_MAX_DEV_NUM*VI_MAX_CHN_NUM)?0:priv_id;
}

static RK_S32 rockchip_vi_chn_get_frame(struct ht_vi *pvi, HT_S32 viDev, HT_S32 viChn, HT_S32 wait_time,
                                        ht_vi_get_frame_cb cb, void *user)
{
    RK_S32 s32Ret = -1;
	struct rockchip_vi *plt_vi = container_of(pvi, struct rockchip_vi, m_vi);
	HT_U32 priv_id = 0;
	struct rockchip_vi_priv *priv = NULL;
	
    if (!cb)
        return -1;

	if (rockchip_vi_check_id_availd(viDev, viChn, 0))
		return -2;

	priv_id = rockchip_vi_priv_id(viDev, viChn);

	if (plt_vi->priv[priv_id])
	{
		printf("vi chn get frame pthread %d %d exists\n", viDev, viChn);
		return -3;
	}
	
	priv = (struct rockchip_vi_priv *)calloc(1, sizeof(struct rockchip_vi_priv));

	if (!priv)
		return -4;

	plt_vi->priv[priv_id] = priv;
	
	priv->dev 		= viDev;
	priv->chn 		= viChn;
	priv->user_cb 	= cb;
	priv->user 		= user;
	priv->wait_time = wait_time;
	
	snprintf(priv->stChnPthreadName, sizeof(priv->stChnPthreadName), "rk_vi_%d_%d_%d", priv_id, viDev, viChn);
	
	s32Ret = ht_pthread_request_work_always(&plt_vi->pthread, priv->stChnPthreadName, 
										rockchip_vi_pthread_chn_get_frame, (HT_VOID *)priv);

	if (s32Ret)
	{
		SYS_FREE_MEM(priv);
		plt_vi->priv[priv_id] = NULL;
	}

	return s32Ret;
}


static HT_U64 rockchip_frame_get_pts(struct ht_vi *pvi, HT_FRAME frame)
{
	VIDEO_FRAME_INFO_S *pinfo = (VIDEO_FRAME_INFO_S *)frame;

	return pinfo->stVFrame.u64PTS;
}

static RK_S32 rockchip_vi_chn_stop_frame(struct ht_vi *pvi, HT_S32 viDev, HT_S32 viChn)
{
    struct rockchip_vi_priv *priv = NULL;
	HT_U32 priv_id = 0;
	struct rockchip_vi *plt_vi = container_of(pvi, struct rockchip_vi, m_vi);
	HT_S32 ret = -1;

	if (rockchip_vi_check_id_availd(viDev, viChn, 0))
		return -1;

	priv_id = rockchip_vi_priv_id(viDev, viChn);
	
	priv = plt_vi->priv[priv_id];

	if (!priv)
		return -2;

	ret = ht_pthread_work_stop(&plt_vi->pthread, priv->stChnPthreadName);
	
	SYS_FREE_MEM(priv);
	plt_vi->priv[priv_id] = NULL;

	return ret;
}

struct ht_vi_ops g_vi_ops = {
    .vi_dev_init 			= rockchip_vi_dev_init,
    .vi_dev_exit 			= rockchip_vi_dev_exit,

#ifndef RK3588
    .vi_get_connecion_info 	= rockchip_vi_connection_info,
#endif
    .vi_chn_init 			= rockchip_vi_chn_init,
    .vi_chn_exit 			= rockchip_vi_chn_exit,
    .vi_chn_get_frame 		= rockchip_vi_chn_get_frame,
    .vi_chn_stop_frame 		= rockchip_vi_chn_stop_frame,

	.vi_frame_get_pts		= rockchip_frame_get_pts,
};

static HT_S32 rockchip_vi_default_config(struct rockchip_vi *plt_vi)
{
	HT_S32 ret = -1;
	
	ret = ht_pthread_init(&plt_vi->pthread, "rockchip_vi");

	if (ret)
	{
		printf("pthread init faild %d\n", ret);
		return -2;
	}

	return 0;
}

__init static RK_S32 rockchip_vi_register(void)
{
	HT_S32 ret = -1;
    struct rockchip_vi *plt_vi = (struct rockchip_vi *)calloc(1, sizeof(struct rockchip_vi));
    struct ht_vi *pvi = NULL;

    if (!plt_vi)
        return -1;

	ret = rockchip_vi_default_config(plt_vi);

	if (ret)
		return ret;

    pvi = &plt_vi->m_vi;

	pvi->m_id = HT_ID_VI;
    pvi->ops = &g_vi_ops;

    ht_vi_modules_register(pvi);

    return ret;
}
